﻿#ifndef QBCI_H
#define QBCI_H

#include <QHash>
#include <QObject>

#include "qbcianalyzer.h"
#include "qbciapp.h"
#include "qbciconfig.h"
#include "qbcisource.h"

namespace bci {
class QBciAnalyzer;
class QBciApp;

/*!
 * \brief Bci任务的顶层控制模块
 *
 * QBci 是每一个Bci任务必须有的一个顶层控制模块
 * 代表整个处理流程
 */
class QBci : public QObject {
  Q_OBJECT
public:
  QBci();
  QBci(const QBci &) = delete;
  QBci(QBci &&) = delete;
  QBci(QBciSource *source, QBciAnalyzer *analyzer, QBciApp *app);

  ~QBci();

  enum ModType {
    Source,   //< 脑电数据源模块
    Analyzer, //< 数据分析模块
    App,      //< 用户模块
    Other     //< 其它类型
  };

  template <ModType t> class T2C { using type = QBciMod; };
  static const QString modTypeName(const ModType &t);

private:
  struct ModInfo {
    QBciMod *mod = nullptr;
    QThread *thread = nullptr;
    ModType type = Other;
  };

  QBciSource *mSrc;
  QBciAnalyzer *mAlz;
  QBciApp *mApp;

  QHash<QString, ModInfo> ModTable;

  QBciContext cxt;

public:
  /**
   * @brief Get the Mod By given type
   *
   * Get the corresponding obj according to
   * the given type
   *
   * @note if type is Other, function will return nullptr
   *
   * @param type
   * @return QBciMod *
   */
  QBciMod *getModBy(const ModType &type);

  /**
   * @brief Get the Mod By object
   *
   * Get the corresponding obj according to
   * the given type
   *
   * @tparam type
   * @return determined by type
   *
   * @code {.cpp}
   * QBciSource *src = getModBy<QBci::Source>();
   * @endcode
   *
   * @note NOT input Other type.
   */
  template <ModType type, typename T = typename T2C<type>::type> T *getModBy();

  /**
   * @brief Get the Mod By id
   *
   * @param id
   * @return QBciMod*
   */
  QBciMod *getModBy(const QString &id);

  /**
   * @brief start processing
   */
  void start();

  /**
   * @brief stop processing
   */
  void stop();

private:
  bool isRunning = false;

signals:
  /**
   * @brief emit when processing started
   */
  void started();
  /**
   * @brief emit when processing stopped
   */
  void stopped();

  /**
   * @brief trigger by start()
   */
  void toStart(QBciContext *cxt);

  /**
   * @brief trigger by stop()
   */
  void toStop();

  /**
   * @brief config updated
   */
  void configUpdated(const ::bci::QBciConfig *);

private:
  /**
   * @brief
   *
   * @return ModInfo* if not find matching modinfo,
   * return nullptr.
   */
  ModInfo *findModuleInfo(const QString &id);
  void privateAddMod(QBciMod *mod, ModType type, bool isAttach);
  void privateAddModInfo(ModInfo i);
  void privateRemoveSAA(ModType type);
  void privateRemoveMod(const QString &id);

public:
  /**
   * @brief Add src as Source Module of QBci
   *
   * @param src
   */
  void addSoruce(QBciSource *src, const bool &isAttach = true);
  /**
   * @brief Add alz as Analyzer Module of QBci
   *
   * @param alz
   */
  void addAnalyzer(QBciAnalyzer *alz, const bool &isAttach = true);
  /**
   * @brief Add app as App Module of QBci
   *
   * @param app
   */
  void addApp(QBciApp *app, const bool &isAttach = true);
  /**
   * @brief Add mod as a submodule of QBci
   *
   * @note if the role of the mod is
   * Source, Analyzer or App, DONOT use
   * this function for adding Mod
   */
  void addMod(QBciMod *mod, const bool &isAttach = true);
  /**
   * @brief remove Source Module
   *
   * set internal Source Module as nullptr
   */
  void removeSource();
  /**
   * @brief remove Analyzer Module
   *
   * set internal Analyzer Module as nullptr
   */
  void removeAnalyzer();
  /**
   * @brief remove App Module
   *
   * set internal App Module as nullptr
   */
  void removeApp();
  /**
   * @brief remove a submod
   *
   * @note if the role of the mod is
   * Source, Analyzer or App, DONOT use
   * this function for removing Mod
   */
  void removeMod(const QString &id);

  /**
   * @brief check if mod has been added to QBci
   *
   * @param mod
   * @note this result depends on the id of mod,
   * not the pointer value.
   */
  bool isContainMod(QBciMod *mod) const;
  bool isContainMod(const QString &id) const;

private:
  void error(const QString &mesg);
  void warn(const QString &mesg);
  void info(const QString &mesg);
  void debug(const QString &mesg);

  QThread *pushThread(const QString &id);
  void popThread(const QString &id);
  QHash<QString, QSharedPointer<QThread>> threadTable;

public slots:
  void updateConfig(const QString &filePath);

private:
  QBciConfig *mConfig;
};

template <> class QBci::T2C<QBci::Source> { using type = QBciSource; };
template <> class QBci::T2C<QBci::Analyzer> { using type = QBciAnalyzer; };
template <> class QBci::T2C<QBci::App> { using type = QBciApp; };
template <> class QBci::T2C<QBci::Other> { using type = QBciMod; };
template <QBci::ModType type, typename T> T *QBci::getModBy() {
  return dynamic_cast<T *>(getModBy(type));
}

} // namespace bci
#endif
